/**
 * RequestLimiter 类
 * 用于控制API请求频率，防止触发API请求限制
 */
export class RequestLimiter {
  private queue: Array<() => Promise<any>> = [];
  private running = false;
  private requestsInLastMinute = 0;
  private lastRequestTime: number = 0;
  private readonly MAX_REQUESTS_PER_MINUTE: number;
  private readonly MIN_DELAY_MS: number;

  /**
   * 创建一个请求限制器实例
   * @param maxRequestsPerMinute 每分钟最大请求数，默认为30
   * @param minDelayMs 两次请求之间的最小间隔(毫秒)，默认为1000ms
   */
  constructor(maxRequestsPerMinute = 30, minDelayMs = 1000) {
    this.MAX_REQUESTS_PER_MINUTE = maxRequestsPerMinute;
    this.MIN_DELAY_MS = minDelayMs;

    // 每分钟重置计数器
    setInterval(() => {
      this.requestsInLastMinute = 0;
    }, 60000);
  }

  /**
   * 添加一个请求到队列中，并在适当的时候执行
   * @param requestFn 请求函数，需要返回Promise
   * @returns 请求函数执行后的Promise结果
   */
  async addRequest<T>(requestFn: () => Promise<T>): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      // 将请求及其resolve/reject函数添加到队列
      this.queue.push(async () => {
        try {
          const result = await this.executeWithLimit(requestFn);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      });

      // 如果队列当前未运行，则开始处理
      if (!this.running) {
        this.processQueue();
      }
    });
  }

  /**
   * 提交服务请求 - 为兼容现有API服务层代码
   * @param requestFn 请求函数
   * @returns 请求结果
   */
  async submitService<T>(requestFn: () => Promise<T>): Promise<T> {
    return this.addRequest(requestFn);
  }

  /**
   * 批量提交请求 - 为兼容现有API服务层代码
   * @param type 请求类型标识（无实质作用，为兼容现有接口）
   * @param requestFunctions 请求函数数组
   * @param options 批处理选项
   * @returns 批量请求的结果数组
   */
  async batchSubmit<T>(
    type: string, 
    requestFunctions: Array<() => Promise<T>>, 
    options: { maxConcurrent?: number; delay?: number } = {}
  ): Promise<T[]> {
    const maxConcurrent = options.maxConcurrent || 1;
    const delay = options.delay || 500;
    const results: T[] = [];
    
    // 使用简单的分批处理
    for (let i = 0; i < requestFunctions.length; i += maxConcurrent) {
      const batch = requestFunctions.slice(i, i + maxConcurrent);
      const batchPromises = batch.map(async (fn) => {
        // 每个请求都通过限流器提交
        return this.addRequest(fn);
      });
      
      // 等待当前批次完成
      const batchResults = await Promise.all(batchPromises);
      results.push(...batchResults);
      
      // 在批次之间添加延迟
      if (i + maxConcurrent < requestFunctions.length) {
        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }
    
    return results;
  }

  /**
   * 在确保不超过限制的情况下执行请求
   * @param requestFn 要执行的请求函数
   * @returns 请求结果的Promise
   */
  private async executeWithLimit<T>(requestFn: () => Promise<T>): Promise<T> {
    // 计算需要等待的时间
    const now = Date.now();
    let delayMs = 0;

    // 确保至少等待MIN_DELAY_MS
    if (this.lastRequestTime > 0) {
      const timeSinceLastRequest = now - this.lastRequestTime;
      if (timeSinceLastRequest < this.MIN_DELAY_MS) {
        delayMs = this.MIN_DELAY_MS - timeSinceLastRequest;
      }
    }

    // 如果接近请求限制，添加额外延迟
    if (this.requestsInLastMinute >= this.MAX_REQUESTS_PER_MINUTE - 5) {
      // 添加指数退避，越接近限制，延迟越长
      const extraDelay = Math.pow(2, this.requestsInLastMinute - this.MAX_REQUESTS_PER_MINUTE + 5) * 500;
      delayMs += Math.min(extraDelay, 10000); // 最多额外延迟10秒
      console.log(`接近API请求限制，添加${delayMs}ms额外延迟`);
    }

    // 如果需要延迟，则等待
    if (delayMs > 0) {
      await new Promise(resolve => setTimeout(resolve, delayMs));
    }

    // 执行请求并更新状态
    this.lastRequestTime = Date.now();
    this.requestsInLastMinute++;
    
    return requestFn();
  }

  /**
   * 处理队列中的请求
   */
  private async processQueue() {
    this.running = true;

    while (this.queue.length > 0) {
      const request = this.queue.shift();
      if (request) {
        try {
          await request();
        } catch (error) {
          console.error('处理请求队列出错:', error);
        }
      }
    }

    this.running = false;
  }

  /**
   * 清空请求队列
   */
  clearQueue() {
    this.queue = [];
  }

  /**
   * 获取队列中待处理的请求数量
   */
  get pendingCount(): number {
    return this.queue.length;
  }
}